using System;
using System.Reflection;
using System.Text;
using Sirenix.Serialization;
using Sirenix.Utilities;
using UnityEngine;

namespace BTEditor
{
    [Serializable]
    public abstract class EditorNode
    {
        public EditorNode()
        {
        }

        /// <summary>
        /// 归属图形化节点
        /// </summary>
        [OdinSerialize]
        protected BTNode BelongNode { get; set; }

        /// <summary>
        /// 节点真实类型
        /// </summary>
        /// <returns></returns>
        public abstract Type GetNodeType();

        /// <summary>
        /// 节点类型
        /// </summary>
        [OdinSerialize]
        public abstract EditorNodeEnum NodeEnum { get; }

        /// <summary>
        /// 节点是否有效
        /// </summary>
        /// <returns></returns>
        public abstract ErrorType GetIsVaild();

        /// <summary>
        /// 可添加节点数量
        /// </summary>
        public abstract int CanAddNodeCount { get; }

        /// <summary>
        /// 设置属于的图形节点
        /// </summary>
        /// <param name="node"></param>
        public void SetBelongNode(BTNode node)
        {
            BelongNode = node;
        }

        /// <summary>
        /// 处理特殊的属性
        /// </summary>
        /// <param name="sb"></param>
        protected virtual void DealField(StringBuilder sb)
        {
            FieldInfo[] fieldInfos = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
            for (int i = 0; i < fieldInfos.Length; i++)
            {
                FieldInfo field = fieldInfos[i];
                BTValueAttribute attr = field.GetAttribute<BTValueAttribute>();
                if (attr == null)
                {
                    continue;
                }

                bool isAddPoint = false;
                if (NodeEnum == EditorNodeEnum.Decorator || NodeEnum == EditorNodeEnum.Composite)
                {
                    isAddPoint = true;
                }
                else
                {
                    isAddPoint = fieldInfos.Length - 1 != i && fieldInfos.Length > 1;
                }

                object value = field.GetValue(this);
                if (value == null)
                {
                    continue;
                }

                switch (attr.DealType)
                {
                    case DealType.OnlyUse:
                        if (value is string)
                        {
                            sb.Append($"\"{value}\"");
                        }
                        else if (value is float f)
                        {
                            sb.Append($"{f.ToArgs()}");
                        }
                        else if (value is bool b)
                        {
                            sb.Append($"{b.ToArgs()}");
                        }
                        else
                        {
                            sb.Append($"{value}");
                        }

                        break;
                    case DealType.ActionToChild:
                        if (value.GetType().IsSubclassOf(typeof(ActionBase)))
                        {
                            if (!(value is ActionBase))
                            {
                                Debug.LogError($"{field.Name} can't cast to ActionBase");
                                continue;
                            }

                            sb.Append($"{((ActionBase) value).ToChild()}");
                        }

                        break;
                    case DealType.EnumValue:
                        if (value is Enum e)
                        {
                            sb.Append($"{e.ToArgs()}");
                        }                      
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                if (isAddPoint)
                {
                    sb.Append(",");
                }
            }
        }

        /// <summary>
        /// 输出代码格式
        /// </summary>
        /// <returns></returns>
        public abstract string ToChild();
    }
}